package main.leetcode.offer.firstround.from03to50;

/**
 * 14-I.剪绳子
 *
 * <p>给你一根长度为 n 的绳子，请把绳子剪成整数长度的 m 段（m、n都是整数，n>1并且m>1），每段绳子的长度记为 k[0],k[1]...k[m] 。请问
 * k[0]*k[1]*...*k[m] 可能的最大乘积是多少？例如，当绳子的长度是8时，我们把它剪成长度分别为2、3、3的三段，此时得到的最大乘积是18。
 *
 * <p>示例 1： 输入: 2 输出: 1 解释: 2 = 1 + 1, 1 × 1 = 1
 *
 * <p>示例 2： 输入: 10 输出: 36 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
 *
 * <p>提示：2 <= n <= 58
 *
 * <p>来源：力扣（LeetCode） 链接：https://leetcode-cn.com/problems/jian-sheng-zi-lcof
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 */
public class ex14I {
    public static void main(String[] args) {
        System.out.println(new ex14I().cuttingRope2(8));
    }

    // 记忆化技术（备忘录法）——自顶向下
    public int cuttingRope(int n) {
        if (n == 2) return 1;
        if (n == 3) return 2;
        int[] mul = new int[n + 1];
        mul[0] = 0;
        mul[1] = 1;
        mul[2] = 1;
        mul[3] = 2;
        for (int i = 4; i <= n; i++) mul[i] = memoization(i, mul);
        return mul[n];
    }

    // 计算mul[][]
    private int memoization(int n, int[] mul) {
        if (mul[n] != 0) return mul[n]; // 备忘录中已记录，直接返回
        int res = -1;
        int i = 1;
        while (i < n) { // 遍历所有组成的可能性，f(n) = f(n-i) * i，找到最大的
            res =
                    Math.max(
                            res,
                            Math.max(
                                    i * (n - i),
                                    i
                                            * memoization(
                                                    n - i,
                                                    mul))); // 一段绳子，至少剪成两段，分段越少往往乘积越大，所以要把i * (n -
                                                            // i)放进来比较
            i++;
        }
        return res;
    }

    // 动态规划——自底向上
    public int cuttingRope1(int n) {
        if (n == 2) return 1;
        if (n == 3) return 2;
        int[] dp = new int[n + 1];
        dp[2] = 1;
        for (int i = 3; i <= n; i++)
            for (int j = 1; j < i; j++)
                dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
        return dp[n];
    }

    // 贪心算法——数学推理
    public int cuttingRope2(int n) {
        if (n == 2) return 1;
        if (n == 3) return 2;
        int res = 1;
        while (n > 4) {
            res *= 3;
            n -= 3;
        }
        return res * n;
    }
}
